﻿using System;
using PhoneNetTools.Utilities;

namespace PhoneNetTools.Dns.Records
{
    /// <summary>
    /// A helper factory that creates both <see cref="Question"/> and resource records from raw byte data.
    /// </summary>
    internal static class ResourceRecordFactory
    {
        /// <summary>
        /// Creates a question from raw byte data, starting at the given offset.
        /// The offset is incremented and points to the data after the raw question data when this method has finished.
        /// </summary>
        /// <param name="rawData">The raw data to extract the question from.</param>
        /// <param name="startIndex">The start index to start extracting the question at.</param>
        /// <returns>A question instance configured from the contents of the raw data.</returns>
        public static Question CreateQuestionFromRaw(byte[] rawData, ref int startIndex)
        {
            var name = DnsStringBuilder.ExtractString(rawData, ref startIndex);
            var qType = ExtractQType(rawData, ref startIndex);
            var qClass = ExtractQClass(rawData, ref startIndex);

            var result = new Question(qType);
            result.Class = qClass;
            result.Name = name;

            // questions do not have custom data, but call the method anyway,
            // just in case it is extended in the future
            startIndex = result.FromRawData(rawData, startIndex);

            return result;
        }

        /// <summary>
        /// Creates a resource record from raw byte data, starting at the given offset.
        /// The offset is incremented and points to the data after the raw resource record when this method has finished.
        /// The actual type of the returned resource record depends on the QTYPE of the detected record.
        /// </summary>
        /// <param name="rawData">The raw data to extract the resource record from.</param>
        /// <param name="startIndex">The start index to start extracting the resource record at.</param>
        /// <returns>An instance of <see cref="GenericResourceRecord"/> or one of its derived types that contains the contents of the resource record from the raw data</returns>
        public static GenericResourceRecord CreateFromRaw(byte[] rawData, ref int startIndex)
        {
            var name = DnsStringBuilder.ExtractString(rawData, ref startIndex);
            var qType = ExtractQType(rawData, ref startIndex);
            var qClass = ExtractQClass(rawData, ref startIndex);

            // now that we know the type, create the correct resource record
            var result = CreateResourceRecord(qType, qClass, name);

            // let the message configure itself
            startIndex = result.FromRawData(rawData, startIndex);
            return result;
        }

        private static QType ExtractQType(byte[] rawData, ref int startIndex)
        {
            var qType = (QType)BigEndianBitConverter.ToUInt16(rawData, startIndex);
            startIndex += sizeof(UInt16);
            return qType;
        }

        private static QClass ExtractQClass(byte[] rawData, ref int startIndex)
        {
            var qClass = (QClass)BigEndianBitConverter.ToUInt16(rawData, startIndex);
            startIndex += sizeof(UInt16);
            return qClass;
        }

        private static GenericResourceRecord CreateResourceRecord(QType qType, QClass qClass, string name)
        {
            // at the moment we only explicitly support type A
            // => more to come in the future(?)
            switch (qType)
            {
                case QType.A:
                    var recordAResult = new ResourceRecordA
                                            {
                                                Class = qClass,
                                                Name = name
                                            };
                    return recordAResult;
                default:
                    var genericResult = new GenericResourceRecord(qType)
                                            {
                                                Class = qClass,
                                                Name = name
                                            };
                    return genericResult;
            }
        }
    }
}
